{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 零噪声外插抑噪方法\n",
    "\n",
    "*版权所有 (c) 2021 百度量子计算研究所，保留所有权利。*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 内容概要\n",
    "\n",
    "**注意：运行本教程程序所花费的时间及 Quntum Hub 点数会根据用户所输入的参数不同而不同。对于本教程给出的默认参数，用户需要花费 28 点数来获得计算结果。想要获取更多点数，请通过 [Quantum Hub](https://quantum-hub.baidu.com) 联系我们。首先，登录 [Quantum Hub](https://quantum-hub.baidu.com)，然后进入“意见反馈”页面，点击“获取点数”，然后输入必要的信息。提交您的反馈并等待回复。**\n",
    "\n",
    "本教程介绍量子计算中一种用以抑制噪声的手段：零噪声外插法（Zero-Noise Extrapolation，ZNE）。我们首先会简明扼要地阐述这一方法的原理依据、实施手段，然后以单比特 Clifford 序列作为基准测试来演示如何在量脉上使用该方法。本教程的大纲如下：\n",
    "\n",
    "- ZNE 理论\n",
    "    - 介绍\n",
    "    - 噪声缩放\n",
    "    - 外插估计\n",
    "- ZNE 实践\n",
    "    - 问题描述\n",
    "    - 量脉（Quanlse）实现\n",
    "- 总结\n",
    "- 参考资料"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "## ZNE 理论\n",
    "\n",
    "### 介绍\n",
    "零噪声外插法（Zero-Noise Extrapolation，ZNE）是量子噪声抑制/量子误差缓释（Quantum Error Mitigation）领域中一种强有力的技术。事实上 ZNE 方法并不直接抑制量子计算过程中的固有噪声，而是能够帮助我们获得等效的噪声缓释之后的计算结果。其主要依据的思想是，我们虽然不知道噪声的具体形式和如何控制噪声源，但如果能够增大或等效地增大错误率，便能够利用不同错误率下的计算结果，外推至错误率为 0 时的计算结果，即理想的计算结果。这也是 ZNE 方法的主要优势所在。这一方法尤其适用于形如 $\\text{Tr}(A\\rho)$ 的期望值计算任务。在多数情况下，ZNE 方法都能够表现出良好的效果，是量子噪声缓释领域一种相当具有代表性的方法 \\[1, 2\\]。\n",
    "\n",
    "该方法的实施流程如下图所示。由图可知，ZNE 方法的实施需要两步：噪声缩放和错误外推。在众多噪声缩放技术中，时域拉伸（time-variant rescaling）是一种效果较稳定且很有应用价值的方法，它要求根据一定的缩放系数在时域上拉伸系统哈密顿量，进而在经历相应的含噪声量子演化后，获得等效的噪声缩放了情况下的量子终态。在外插估计阶段，为求简便，我们将使用 Richardson 外插方法。这是数值算法领域中一种理论上能够消除任意低阶估计误差的外插算法。值得一提的是，在某些特定的假设下，其他类型的外插方法如多项式外插、指数外插等也有较好的效果 \\[3\\]。\n",
    "\n",
    "![zne-profile](figures/zne-profile.png)\n",
    "\n",
    "\n",
    "### 噪声缩放\n",
    "\n",
    "含噪声量子计算过程在物理层面上的动力学演化（$t\\in [0,T]$）需要以 Lindblad 主方程：\n",
    "$$\n",
    "\\frac{\\partial}{\\partial t}\\rho(t) = -i[K,\\rho]+\\lambda\\mathcal{L}(\\rho),\n",
    "$$\n",
    "来刻画，其中含时哈密顿量 $K$ 与密度算符 $\\rho$ 的对易子部分代表了我们所期望的相干演化过程（对应量子算法中的目标量子电路），而 Lindblad 算子 $\\mathcal{L}$ 描述了我们想要抑制的噪声过程。\n",
    "需要强调的是，我们并不需要知道算子 $\\mathcal{L}$ 的确切形式，而只需假定这一耗散项不显含时间，并且该耗散过程的速率由标量参数 $\\lambda$ 来表征。将噪声系数为 $\\lambda$ 时量子体系演化至终止时刻 $T$ 时的量子态记为 $\\rho_\\lambda (T)$。给定任一正比系数 $c$ ，我们能否获得噪声速率缩放 $\\lambda\\to c\\lambda$ 情况下的量子终态 $\\rho_{c\\lambda}(T)$？幸运的是，当 $\\mathcal{L}$ 的形式不显含时间时，将演化时间拉长至 $cT$，同时对哈密顿量做“时域拉伸、幅值压缩”的操作：\n",
    "$$\n",
    "K(t)\\to K^\\prime(t)= \\frac{K(t/c)}{c},\n",
    "$$\n",
    "得到的量子终态 $\\rho^\\prime (cT)$ 在数值上就等于噪声系数放大情况下的量子终态 $\\rho_{c\\lambda}(T)$ \\[1\\]。\n",
    "\n",
    "对应到实验上，量子体系的哈密顿量包括了不含时的驱动项和含时的控制项，后者一般以驱动脉冲的形式被施加到实际的量子体系中。则时域上拉伸控制哈密顿量的操作实际上是通过拉伸对应的各个通道的控制脉冲来实现的。例如，根据量脉 [单比特量子门](https://quanlse.baidu.com/#/doc/tutorial-single-qubit) 一章中的结果，Hadamard 门：\n",
    "$$\n",
    "H=\\frac{1}{\\sqrt{2}}\\begin{pmatrix}\n",
    "1&1\\\\\n",
    "1&-1\n",
    "\\end{pmatrix},\n",
    "$$\n",
    "可以通过施加具有时间差的 $X$ 通道和 $Y$ 通道的驱动脉冲来实现。也就是说，拉伸 Hadamard 门对应的控制哈密顿量需要拉伸所有通道的驱动脉冲。下图以缩放系数 $1$（不做缩放）、$1.25$、$1.5$ 为例，展示了拉伸后的各通道脉冲相比原脉冲在时域上扩展、幅度上压缩的效果，其拉伸和压缩的倍数相同——这是为了保证拉伸后的量子门具有与原始量子门有相同的幺正变换效果。\n",
    "\n",
    "![zne-profile](figures/zne-pulse-rescale-h.png)\n",
    "\n",
    "需要指出，我们所关心的量子计算结果 $E(\\lambda)$ 并非一定要直接表示为噪声系数的函数，即 $\\lambda$ 这一参数也可以是其他物理量，如失真度、温度、差错概率、变分参数等。在后面量脉中的实例程序中，我们实际上是针对量子电路相对于理想幺正演化的失真度来做缩放和外插的。\n",
    "\n",
    "### 外插估计\n",
    "\n",
    "在数值算法领域，Richardson 外插法是一种能够普遍性地消除低阶估计误差的有效方法。在 Richardson 外插法的框架下，需要假定量子计算结果 $E(\\lambda)$ 关于理想值 $E^\\ast \\equiv  E(\\lambda=0)$ 的估计误差表示为 $\\lambda$ 的幂级数形式\n",
    "$$\n",
    "E(\\lambda)=\\sum_{k=0}^{d} a_k \\lambda^k + O(\\lambda^{d+1}),\n",
    "$$\n",
    "其中 $a_0=E^\\ast$；$\\left\\{ a_k \\right\\}_k$ 是一系列待定参数；$d$ 是我们想要通过外插消除的误差的阶数。如果我们能够获得一组 $\\lambda$ 取不同参数值时对应的估计量 $\\left\\{E(\\lambda_j)\\right\\}_{j=1}^{d+1}$（比如使用上文所述的噪声缩放方法），便能够逐步迭代地构造出一个表示为这一系列估计量线性组合形式的新估计量 $E^d(\\lambda)$。与原估计量相比，$E^d(\\lambda)$ 有着更高精度的估计误差 \\[4\\]。\n",
    "\n",
    "![extrapolation](figures/zne-extrapolation.png)\n",
    "\n",
    "上图中我们设置了阶数为 $2$ 来演示 Richardson 外插法的基本原理。从中我们能够定性地看出外插算法的基本思想—— $\\lambda =0$ 的数据点是通过对一系列含噪数据拟合外推得到的。如前所述，Richardson 外插法只是众多外插方法中的一种，并且只有在幂级数关系假设成立的条件下才能表现出良好的效果。在上述 Lindblad 主方程的分析框架下，文献 \\[1\\] 为这一假设提供了有力的理论支持。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## ZNE 实践"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 问题描述"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "**Clifford 随机量子电路**\n",
    "\n",
    "\n",
    "Clifford 序列包含了一组连续的随机 Clifford 量子门，常常被用作量子电路性能的基准测试。为了便于定标理想的力学量期望值，我们考虑包含了长度为 $n$ 的 Clifford 随机序列和一个相应逆运算的恒等（identity-equivalent）量子电路。如下图所示，$C_j$ 指代随机生成的 Clifford 幺正门，$C_{\\rm inv}$ 指代前 $n$ 个 Clifford 量子门连续作用效果的等效逆门，使得\n",
    "$$\n",
    "C_{\\rm inv}C_n C_{n-1}\\cdots C_1=I.\n",
    "$$\n",
    "\n",
    "\n",
    "**计算任务**\n",
    "\n",
    "\n",
    "我们将测试 ZNE 方法在初态为 $|0\\rangle =\\begin{pmatrix} 1\\\\0\\end{pmatrix}$、测量算符为 $|0\\rangle$ 态投影子 $A=\\begin{pmatrix} 1&0\\\\0&0\\end{pmatrix}$、长度为 $n+1$ 的恒等量子电路条件下的表现效果。\n",
    "\n",
    "![zne-clifford-circuit](figures/zne-clifford-circuit.png)\n",
    "\n",
    "理想情况下，量子态经过任意深度的恒等量子电路的作用，所得到的终态仍然会是 $|0\\rangle$，测量算子的理想期望值也总保持为 $\\langle A\\rangle_{\\rm ideal}=1$。\n",
    "\n",
    "由于量子噪声的存在，并且施加更多的量子门会引入更大的噪声效应，恒等量子电路越深，量子系统终态会愈加偏离 $|0\\rangle$，期望值 $\\langle A\\rangle_{\\rm noisy}$ 的结果将会越偏离 $1$。\n",
    "\n",
    "接下来我们将展示如何借助量脉云服务所提供的 ZNE 方法在数值结果上来有效地抑制量子计算的噪声，使得即便在较深的 Clifford 电路情况下，抑噪后的期望值相对于理想值仍具有很高的估计精度。\n",
    "\n",
    "**数据处理**\n",
    "\n",
    "接下来我们将详细描述数据处理过程，借此充分体现量脉中 ZNE 方法的效力。具体计算策略为，首先生成总长为 $n$ 的随机 Clifford 量子门序列，而后依次选取前 $k$ ($1\\le k\\le n$) 个量子门并计算对应的逆门，构造出长为 $k+1$ 的恒等量子电路。对于如此一个量子电路，我们计算末态下的力学量期望，并通过等效噪声缩放手段得到缩放系数从 $1$ 到 $d+1$ 情况下的含噪期望值，而后通过 Richardson 外插方法计算得阶数从 $1$ 到 $d$ 的外插值。最终我们将会得到 $n\\cdot d$ 个外插值和 $n\\cdot(d+1)$ 个噪声缩放值。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 量脉（Quanlse）实现"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "**导入必要的模块和函数**\n",
    "\n",
    "\n",
    "在您成功安装最新版的量脉后，您可以按照本教程运行以下程序。为了顺利运行本教程，您需要从量脉（Quanlse）和相关的 Python 库中带入以下模块或和函数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from Quanlse.remoteZNE import remoteZNEMitigation as zneMitigation\n",
    "from Quanlse.ErrorMitigation.ZNE.Extrapolation import extrapolate\n",
    "from Quanlse.ErrorMitigation.Utils.Utils import computeIdealExpectationValue, \\\n",
    "    computeIdealEvolutionOperator, fromCircuitToHamiltonian, randomCircuit, \\\n",
    "    computeInverseGate\n",
    "from Quanlse.ErrorMitigation.Utils.Visualization import plotZNESequences\n",
    "\n",
    "from Quanlse.Utils.Functions import project, expect\n",
    "from Quanlse.Utils.Infidelity import unitaryInfidelity\n",
    "\n",
    "from Quanlse.remoteSimulator import remoteSimulatorRunHamiltonian\n",
    "\n",
    "import numpy as np\n",
    "from copy import deepcopy"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在本地设备上完成 ZNE 方法所需要的优化过程通常需要很长时间，而我们提供的云服务可以显著加速此过程。要使用量脉云服务，用户需要从 http://quantum-hub.baidu.com 获取 token。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from Quanlse import Define\n",
    "\n",
    "Define.hubToken = ''"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**构造 Clifford 随机量子电路**\n",
    "\n",
    "基于所构造的量子电路和其他初始参数，我们可以计算得到理想演化后的力学量期望值和含噪声演化后的期望值，同时也能计算实际量子电路演化和理想演化之间的失真度（infidelity）。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Set the maximal length of the random Clifford circuit\n",
    "numSeq = 5\n",
    "numQubits = 1\n",
    "\n",
    "# Set the input state as |0> and the quantum observable as |0><0|\n",
    "state = np.diag([1, 0]).astype(complex) \n",
    "A = np.diag([1, 0]).astype(complex) \n",
    "\n",
    "# Set the maximal extrapolation order\n",
    "order = 2\n",
    "\n",
    "# Considering the reproducibility of our calculation result, we may as well set the \"random seed\" as a fixed value (e.g. 123)\n",
    "circuit = randomCircuit(qubits=1, numSeq=numSeq, seed=123)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**对比理想演化和含噪演化**\n",
    "\n",
    "对于任意长度的量子电路（`circuit`），可利用 `computeInverseGate` 得到其等效逆门，并将此加入到原 `circuit` 得到恒等量子电路。基于所构造的量子电路，我们可以得到理想演化末态的期望值和含噪演化末态的期望值，同时也能计算实际演化过程和理想演化之间的失真度（infidelity）。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Construct the identity-equivalent quantum circuit by appending an inverse gate to the end\n",
    "circuitIdentity = circuit + [computeInverseGate(circuit)]\n",
    "\n",
    "# Compute the ideal expectation value (should be 1.0) and the ideal evolution operator\n",
    "valueIdeal = computeIdealExpectationValue(state, circuitIdentity, A)\n",
    "unitaryIdeal = computeIdealEvolutionOperator(circuitIdentity)\n",
    "\n",
    "# Compute the optimized Hamiltonian for implementing the quantum circuit\n",
    "# The built-in Quanlse Scheduler will be called\n",
    "ham = fromCircuitToHamiltonian(circuitIdentity)\n",
    "\n",
    "# Use the given Hamiltonian to compute the implemented evolution unitary, the infidelity, and the noisy expectation value\n",
    "result = remoteSimulatorRunHamiltonian(ham)\n",
    "unitaryNoisy = project(result.result[0][\"unitary\"], ham.subSysNum, ham.sysLevel, 2)\n",
    "infid = unitaryInfidelity(unitaryIdeal, unitaryNoisy, numQubits)\n",
    "noisyValue = expect(A, unitaryNoisy @ state @ unitaryNoisy.conj().T)\n",
    "\n",
    "# Print the ideal and noisy expectation values\n",
    "print(\"The ideal expectation value: {}; The noisy expectation: {}\".format(valueIdeal, noisyValue))\n",
    "print(\"The ideal evolutionary operator:\")\n",
    "print(unitaryIdeal.round(3))\n",
    "print('The noisy evolutionary operator:')\n",
    "print(unitaryNoisy.round(3))\n",
    "print(\"The implemented evolution unitary has infidelity: \", infid)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**ZNE 方法抑制噪声**\n",
    "\n",
    "显然从以上结果我们能够看出理想期望值和含噪期望值之间存在一定的偏差，而 Richardson 算法能够帮助我们消除这种偏差，从而得到具有任意估计精度的外插值。\n",
    "\n",
    "利用量脉的 `extrapolate` 函数，我们可以根据给定的一组缩放系数和噪声值计算外插值。与含噪声期望值比较，该外插值相对于理想值将具有更高的估计精度。在量脉中，整个 ZNE 方法的实现被封装在 `zneMitigation` 中，是一个高度抽象的接口。`zneMitigation` 内部已经包含了噪声缩放和外插功能，它返回一个噪声抑制后的期望值（$d$ 阶外插标量值）、一组失真度（含有 $d+1$个元素的列表）和一组噪声缩放下的期望值（含有 $d+1$ 个元素的列表）。\n",
    "\n",
    "根据我们的计算策略，以下代码单元中需要执行 `numSeq` 次 `zneMitigation` 函数，即进行 `numSeq` 次数的哈密顿量优化计算，是比较耗费时间的。因此，我们使用量脉云服务加速这一计算过程。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "EsRescaled = []        # EsRescaled size: [numSeq, order + 1]\n",
    "EsExtrapolated = []    # EsExtrapolated size: [numSeq, order]\n",
    "EsIdeal = []           # EsIdeal size: [numSeq,]\n",
    "Infidelities = []      # Infidelities size: [numSeq, order + 1]\n",
    "\n",
    "for length in range(1, numSeq + 1):\n",
    "    print('==' * 20)\n",
    "    print(\"Clifford circuit length:\", length)\n",
    "    # For each sequence, append the equivalent-inverse gate of all the preceding quantum gates\n",
    "    # For each sequence, its length becomes: [1, 2, ..., numSeq] + 1\n",
    "    circuitPart = deepcopy(circuit[:length])\n",
    "    lastGate = computeInverseGate(circuitPart)\n",
    "    circuitPart.append(lastGate)\n",
    "\n",
    "    # Compute ideal expectations firstly for subsequent comparison in figure\n",
    "    EsIdeal.append(computeIdealExpectationValue(state, circuitPart, A))\n",
    "\n",
    "    # Temporary extrapolated values of each order for each-length circuit\n",
    "    mitigatedValues = []\n",
    "    \n",
    "    # Use the Scheduler to compute the optimal Hamiltonian for this circuit\n",
    "    ham = fromCircuitToHamiltonian(circuitPart)\n",
    "\n",
    "    # Rescale order: [c_0, c_1, ..., c_d]; extrapolation order: d\n",
    "    mitigatedValueHighest, infidelities, noisyValues = zneMitigation(state, circuitPart, A, ham=ham, order=order)\n",
    "\n",
    "    # Rescale order: [c_0, c_1], [c_0, c_1, c_2], ...., [c_0, ..., c_{d-1}]\n",
    "    # for d in [1, ..., d - 1]:\n",
    "    for d in range(1, order):\n",
    "        mitigatedValue = extrapolate(infidelities[:(d + 1)], noisyValues[:(d + 1)], type='richardson', order=d)\n",
    "        mitigatedValues.append(mitigatedValue)\n",
    "\n",
    "    mitigatedValues.append(mitigatedValueHighest)\n",
    "\n",
    "    EsExtrapolated.append(mitigatedValues)\n",
    "    EsRescaled.append(noisyValues)\n",
    "    Infidelities.append(infidelities)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**结果与讨论**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# X-axis represents length of quantum circuit, Y-axis represents expectation values\n",
    "plotZNESequences(EsRescaled, EsExtrapolated, EsIdeal, fileName='zne-single-qubit-clifford')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "从以上结果能够看出，我们的噪声缩放策略和外插手段确实能够提高期望值的估计精度。另外，噪声缩放系数越大，期望值相对于理想值的偏差也越大。这一结果反映出噪声放大策略会得到精度更低的哈密顿量，因为原本的哈密顿量已经是量脉调度器优化得到的最优值。而外插法抑制噪声的效果是明显的，并且单纯的一阶或二阶外插就能够得到精度足够高的结果。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "细心的读者或许能够注意到，以上外插结果图示中未经噪声缩放处理的情况下所得到的力学量期望已十分接近理想值，这是因为量脉能够计算得到具有极高保真度的单量子比特控制哈密顿量。以下我们仅使用 $2$ 阶和 $3$ 阶噪声缩放后的期望值来计算 $1$ 阶外插值。由结果可知如此得到的结果同样与理想值相当接近，这也正体现了 Richardson 外插法的明显效果。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "InfidelitiesPartial = np.array(Infidelities)[:, 1:]\n",
    "EsRescaledPartial = np.array(EsRescaled)[:, 1:]\n",
    "orderPartial = order - 1\n",
    "EsExtrapolatedPartial = []    # size: [numSeq, order + 1]\n",
    "for i in range(numSeq):\n",
    "    mitigatedValues = []\n",
    "    for d in range(1, orderPartial + 1):\n",
    "        mitigatedValue = extrapolate(InfidelitiesPartial[i][:(d + 1)], EsRescaledPartial[i][:(d + 1)], type='richardson', order=d)\n",
    "        mitigatedValues.append(mitigatedValue)\n",
    "    EsExtrapolatedPartial.append(mitigatedValues)\n",
    "\n",
    "plotZNESequences(EsRescaledPartial, EsExtrapolatedPartial, EsIdeal, fileName='zne-single-qubit-clifford-2')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 总结\n",
    "\n",
    "本教程介绍了如何在量脉中使用 ZNE 方法提高量子计算结果的精度，并可视化结果。用户可以点击这个链接 [tutorial-ZNE-cn.ipynb](https://github.com/baidu/Quanlse/blob/main/Tutorial/CN/tutorial-ZNE-cn.ipynb) 跳转到此 Jupyter Notebook 文档相应的 GitHub 页面来获取相关代码，并尝试不同于本教程的参数值来进一步探索量脉中 ZNE 模块的功能。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 参考资料\n",
    "\n",
    "\\[1\\] [Temme, K., et al. (2017). \"Error mitigation for short-depth quantum circuits.\" *Physical Review Letters* 119(18): 180509](https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.119.180509).\n",
    "\n",
    "\\[2\\] [Kandala, A., et al. (2019). \"Error mitigation extends the computational reach of a noisy quantum processor.\" *Nature* 567(7749): 491-495](https://www.nature.com/articles/s41586-019-1040-7).\n",
    "\n",
    "\\[3\\] [Giurgica-Tiron, T., et al. (2020). \"Digital zero noise extrapolation for quantum error mitigation.\" 2020 IEEE International Conference on Quantum Computing and Engineering (QCE)](https://ieeexplore.ieee.org/document/9259940).\n",
    "\n",
    "\\[4\\] [A. Sidi (2003). \"Practical Extrapolation Methods: Theory and Applications.\" Cambridge Monographs on Applied and Computational Mathematics, Vol. 10](https://www.cambridge.org/core/books/practical-extrapolation-methods/21A93C2B0793CF09B2F3ABEF78F3F9B9)."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.12"
  },
  "metadata": {
   "interpreter": {
    "hash": "5b0e014d5ba87cb4b3c054330af2bda1402f8a20fe09a5e22b4d18d5eb0c6091"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
